#include <algorithm>
#include <nice2d/sprite.h>
#include <nice2d/tostring.h>
#include <nice2d/components/country.h>

class CountryData
{
public:
    std::wstring name;
    std::wstring desc;
    sf::Color color;
    bool active = true;
    std::list<sf::Color> alliances;
    std::list<SpriteWeakPointer> sprites;
};

Country::Country(const sf::Color &color, const std::wstring &name, const std::wstring &desc):
    data(new CountryData())
{
    data->color = color;
    data->name = name;
    data->desc = desc;
}

Country::~Country()
{
}

std::wstring Country::getName() const
{
    return data->name;
}

sf::Color Country::getColor() const
{
    return data->color;
}

void Country::formAlliance(std::shared_ptr<Country> other, bool alliance)
{
    if (!other)
        return;

    if (alliance) {
        data->alliances.push_back(other->getColor());
        other->data->alliances.push_back(getColor());
    } else {
        data->alliances.remove(other->getColor());
        other->data->alliances.remove(getColor());
    }
}

bool Country::isAllied(const sf::Color &color)
{
    return std::find(data->alliances.begin(), data->alliances.end(), color) != data->alliances.end();
}

bool Country::isAllied(std::shared_ptr<Country> other)
{
    if (!other)
        return false;

    return other->isAllied(data->color);
}

void Country::inactivate()
{
    data->active = false;
}

void Country::addSprite(SpritePointer sprite)
{
    if (sprite && sprite->getSpriteColor() == data->color)
        data->sprites.push_back(sprite);
}

void Country::removeSprite(SpritePointer sprite)
{
    if (sprite && sprite->getSpriteColor() == data->color) {
        data->sprites.remove_if([&sprite](std::weak_ptr<Sprite> current)->bool {
            if (!current.expired() && current.lock() == sprite)
                return true;
            return false;
        });
    }
}

bool Country::isEmpty() const
{
    return data->sprites.empty();
}

void Country::tidyup()
{
    data->sprites.remove_if([](std::weak_ptr<Sprite> current)->bool {
        if (current.expired())
            return true;
        return false;
    });
}

void Country::accept(SpriteVisitor *visitor)
{
    std::for_each(data->sprites.begin(), data->sprites.end(), [&](std::weak_ptr<Sprite> sprite) {
        if (!sprite.expired())
            visitor->visitSprite(sprite.lock());
    });
}

PTree Country::serialize()
{
    PTree node;
    node.add("name", toString(data->name));
    node.add("desc", toString(data->desc));
    node.add_child("color",serializeObject(data->color));
    node.add("active", data->active);

    PTree alliances;
    std::for_each(data->alliances.begin(),
    data->alliances.end(), [&alliances](const sf::Color & color) {
        alliances.add_child("color",serializeObject(color));
    });
    node.add_child("alliances", alliances);
    return node;
}

void Country::deserialize(const PTree &root)
{
    auto name = root.get<std::string>(std::string("name"));
    data->name = toWString(name);

    auto desc = root.get<std::string>(std::string("desc"));
    data->desc = toWString(desc);

    auto color = root.get_child_optional("color");
    if (color.has_value())
        data->color = deserializeColor(color.value());

    auto alliances = root.get_child("alliances");
    data->active = root.get<bool>(std::string("active"));
}

class CountryManagerData
{
public:
    std::vector<std::shared_ptr<Country>> countries;
    uint32_t current = 0;
};

CountryManager::CountryManager():
    data(new CountryManagerData())
{
}

CountryManager::~CountryManager()
{
}

std::shared_ptr<Country> CountryManager::createCountry(const sf::Color &color,
                                                       const std::wstring &name, const std::wstring &desc)
{
    auto country = std::make_shared<Country>(color, name, desc);
    data->countries.push_back(country);
    return country;
}

void CountryManager::updateCurrentCountry()
{
    if (data->countries.empty()) {
        data->current = -1;
        return;
    }

    AllSpriteVisitor visitor;
    auto current = data->countries[data->current];
    current->accept(&visitor);
    auto sprites = visitor.getSprites();
    std::for_each(sprites.begin(), sprites.end(), [](SpritePointer sprite) {
        sprite->setDone(false);
    });

    data->current ++;
    if (data->current >= data->countries.size())
        data->current = 0;

    current = data->countries[data->current];
    current->tidyup();

    if (current->isEmpty()) {
        death(data->countries[data->current]);
        data->countries.erase(data->countries.begin() + data->current);
    }
}

std::shared_ptr<Country> CountryManager::getCurrentCountry()
{
    if (data->current < 0 || data->current >= data->countries.size())
        return nullptr;
    return data->countries[data->current];
}

void CountryManager::update(float deltaTime)
{
}

PTree CountryManager::serialize()
{
    PTree node;
    node.add<int32_t>(std::string("current"), data->current);

    PTree items;
    for (auto itr = data->countries.begin(); itr != data->countries.end(); itr++) {
        items.add_child(std::string("country"), (*itr)->serialize());
    }
    node.add_child("countries", items);
    return node;
}

void CountryManager::deserialize(const PTree &node)
{
    auto current = node.get_optional<int32_t>(std::string("current"));
    if (current.has_value())
        data->current = current.value();

    auto list = node.get_child_optional(std::string("countries"));
    for (const auto &item : list.value()) {
        auto son = item.second;
        sf::Color color = deserializeColor(son.get_child("color"));
        std::string name = son.get<std::string>("name");
        auto country = createCountry(color, toWString(name));
        country->data->active = son.get<bool>("active");

        for (const auto &unit : son.get_child("alliances")) {
            color = deserializeColor(unit.second);
            country->data->alliances.push_back(color);
        }
    }
}

std::shared_ptr<Country> CountryManager::getCountry(const sf::Color &color)
{
    auto itr = data->countries.begin();
    while (itr != data->countries.end()) {
        if ((*itr)->getColor() == color)
            return *itr;
        itr ++;
    }
    return nullptr;
}
